package com.gitee.wsl.io.source

import kotlinx.io.RawSource
import kotlinx.io.Source
import kotlinx.io.buffered
import kotlinx.io.files.Path
import kotlinx.io.files.SystemFileSystem
import kotlinx.io.readLine

/**
 * An abstract representation of a potentially large text-based file that can be read line by line.
 */
@OptIn(ExperimentalStdlibApi::class)
class LargeTextSource(private val reader: Source) : Iterable<String?>, AutoCloseable {

    private var nextLine: String? = null

    /**
     * Constructs a new large file from its path.
     *
     * @param filePath the file path
     * @throws Exception in case the file can not be accessed
     */
    constructor(filePath: String):this(SystemFileSystem.source(Path(filePath)).buffered())

    /**
     * Constructs a new large file from its input stream of bytes.
     *
     * @param inputStream the input stream
     */
    constructor(inputStream: RawSource): this(inputStream.buffered())

    init {
        readNextLine()
    }

    /**
     * Closes this large file and releases any system resources associated with it.
     */
    override fun close() {
        try {
            reader.close()
        } catch (ex: Exception) {
        }
    }

    /**
     * Returns an iterator over the lines of the file.
     *
     * @return the iterator to read the lines of the file
     */
    override fun iterator(): MutableIterator<String?> {
        return FileIterator()
    }

    private fun readNextLine() {
        try {
            nextLine = reader.readLine()
        } catch (ex: Exception) {
            nextLine = null
            throw IllegalStateException("Error reading file", ex)
        }
    }

    private inner class FileIterator : MutableIterator<String?> {
        override fun hasNext(): Boolean {
            return nextLine != null
        }

        override fun next(): String? {
            val currentLine = nextLine
            readNextLine()
            return currentLine
        }

        override fun remove() {
        }
    }
}

fun RawSource.toLargeTextSource(): LargeTextSource {
    return LargeTextSource(this)
}
fun Source.toLargeTextSource(): LargeTextSource {
    return LargeTextSource(this)
}
